/*  
 *  UCIConsole.cs - Text UI for playing against ACE.
 * 
 *  Copyright(c) 2007 Adam W Adair
 *
 *  This file is part of Adam's Chess Engine (ACE).
 *
 *  ACE is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *   
 *  ACE is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with ACE; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
using System;
using System.Collections.Generic;
using System.Text;
using ACE.Board;
using ACE.Engine;

namespace ACE
{
    class UCIConsole : ProtocolConsole
    {
        private const string UCI_VERSION = "ACE";
        ACEEngine m_engine;

        public UCIConsole(ChessGame game):base(game)
        {
            m_engine = new ACEEngine(game);
            m_engine.OnMove += this.OnMoveHandler;
            DebugOn();
        }

        private void SendUCIOptions()
        {
            Console.WriteLine("");
            WriteLine("id name ACE 0.1");
            WriteLine("id author Adam Adair");
            //WriteLine("option name Hash type spin default 16 min 4 max 1024");
            //WriteLine("option name Search Time type spin default 0 min 0 max 3600");    
            WriteLine("option name Search Depth type spin default 0 min 0 max 20");
            //WriteLine("option name Ponder type check default false");
            WriteLine("option name OwnBook type check default true");
            WriteLine("option name BookFile type string default book.txt");
            //WriteLine("option name MultiPV type spin default 1 min 1 max 10");
            //WriteLine("option name NullMove Pruning type combo default Always var Always var Fail High var Never");
            //WriteLine("option name NullMove Reduction type spin default 3 min 1 max 4");
            //WriteLine("option name Verification Search type combo default Always var Always var Endgame var Never");
            //WriteLine("option name Verification Reduction type spin default 5 min 1 max 6");
            //WriteLine("option name History Pruning type check default true");
            //WriteLine("option name History Threshold type spin default 70 min 0 max 100");
            //WriteLine("option name Futility Pruning type check default true");
            //WriteLine("option name Futility Margin type spin default 100 min 0 max 500");
            //WriteLine("option name Extended Futility Margin type spin default 300 min 0 max 900");
            //WriteLine("option name Delta Pruning type check default true");
            //WriteLine("option name Delta Margin type spin default 50 min 0 max 500");
            //WriteLine("option name Quiescence Check Plies type spin default 1 min 0 max 2");
            //WriteLine("option name Material type spin default 95 min 0 max 400");
            //WriteLine("option name Piece Activity type spin default 105 min 0 max 400");
            //WriteLine("option name King Safety type spin default 100 min 0 max 400");
            //WriteLine("option name Pawn Structure type spin default 100 min 0 max 400");
            //WriteLine("option name Passed Pawns type spin default 100 min 0 max 400");
            //WriteLine("option name Toga Lazy Eval type check default true");
            //WriteLine("option name Toga Lazy Eval Margin type spin default 200 min 0 max 900");
            //WriteLine("option name Toga King Safety type check default true");
            //WriteLine("option name Toga King Safety Margin type spin default 1700 min 500 max 3000");
            //WriteLine("option name Toga Extended History Pruning type check default false");
            WriteLine("uciok");
        }

        public void RunMe()
        {
            // First we send the ID information
            // WriteLine("id author Adam Adair");
            // WriteLine("id name ace 0.1");
            // WriteLine("option name Hash type spin default 1 min 1 max 128");
            // TODO:  Options go here.
            // WriteLine("uciok");
            SendUCIOptions();

            for (; ; )
            {
                try
                {
                    //Loop until quit
                    // One of the nice things about UCI is that we don't do anything
                    // without explicitly being told by the GUI.
                    string command = ReadCommand();
                    switch (command)
                    {
                        case "isready":
                            WriteLine("readyok");
                            break;

                        case "ucinewgame":
                            Game.init();
                            break;
                        case "position":
                            Position();
                            break;
                        case "go":
                            Go();
                            break;
                        case "stop":
                            break;
                        case "ponderhit":
                            break;
                        case "debug":
                            handle_debug();
                            break;
                        case "quit":
                            Quit(0);
                            break;
                        case "setoption":
                            break;
                        default:
                            info_string("Unknown command: " + command);
                            break;
                    }

                }
                catch (Exception ex)
                {
                    // we can't write this to console.
                    Log.DefaultLog.LogException(ex);
                    info_string("ERROR: " + ex.Message);
                }
            }
        }

        private void Go()
        {
            //m_engine.go();
            m_engine.Think();
            if (m_engine.NextMove != null)
            {
                string move = "bestmove " + m_engine.NextMove.MoveString();
                WriteLine(move);
            }
            else
            {
                WriteLine("bestmove 0000");
            }
        }

        private void Position()
        {
            string[] args = LastCommandArguments();
            if (args.Length == 0) return;
            int indx=0;
            string type = args[indx++];
            if (type == "fen")
            {
                string fen = "";
                for (indx = 1; indx < 7; ++indx)
                {
                    if (indx > 1) fen += " ";
                    fen += args[indx];
                }
                Game.FEN_Position(fen);
            }
            else if (type == "startpos")
                Game.init();
            else
                return;
            if(args[indx++]=="moves")
            {
                while (indx < args.Length)
                {
                    Game.TryMove(args[indx]);
                    ++indx;
                }
            }
        }

        private void info_string(string str)
        {
            WriteLine("info string " + str);
        }

        private void handle_debug()
        {
            string[] args = LastCommandArguments();
            if (args.Length == 0) return;
            if (args[0] == "on") DebugOn();
            else if (args[0] == "off") DebugOff();
        }

        public void OnMoveHandler(object sender, ACEEventArgs e)
        {
            string move = "bestmove " + e.values[0];
            if (e.values.Count > 1)
            {
                move += " ponder " + e.values[1];
            }
            WriteLine(move);
        }

    }
}
